/*

  Implementations for a mesh.

  @author Richard Kelley, Liesl Wigand

*/

#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <iostream>

#include "mesh.h"
#include "vertex.h"
#include <QGLWidget>
#define SCALE 35.0                     //sizing for meshes loaded: change this to change size of meshes

namespace blackjack {

    void Mesh::draw(){

        std::vector<polygonFace>::iterator iter;          //create iterator to go through each face
        for (iter = faces->begin(); iter != faces->end(); ++iter) {
            //get all the points into the correct temp variables
            //(vertex and normal at vertex)
            //Vertex
            GLfloat x0 = vertices->at((*iter)[0]-1)->x;
            GLfloat y0 = vertices->at((*iter)[0]-1)->y;
            GLfloat z0 = vertices->at((*iter)[0]-1)->z;
            //Normal
            GLfloat xn0 = vertexNormals->at((*iter)[3]-1)->x;
            GLfloat yn0 = vertexNormals->at((*iter)[3]-1)->y;
            GLfloat zn0 = vertexNormals->at((*iter)[3]-1)->z;

            GLfloat x1 = vertices->at((*iter)[1]-1)->x;
            GLfloat y1 = vertices->at((*iter)[1]-1)->y;
            GLfloat z1 = vertices->at((*iter)[1]-1)->z;

            GLfloat xn1 = vertexNormals->at((*iter)[4]-1)->x;
            GLfloat yn1 = vertexNormals->at((*iter)[4]-1)->y;
            GLfloat zn1 = vertexNormals->at((*iter)[4]-1)->z;

            GLfloat x2 = vertices->at((*iter)[2]-1)->x;
            GLfloat y2 = vertices->at((*iter)[2]-1)->y;
            GLfloat z2 = vertices->at((*iter)[2]-1)->z;

            GLfloat xn2 = vertexNormals->at((*iter)[5]-1)->x;
            GLfloat yn2 = vertexNormals->at((*iter)[5]-1)->y;
            GLfloat zn2 = vertexNormals->at((*iter)[5]-1)->z;

            //set color for drawing
            glColor3fv(color);
            //glNormal3f(xn0, yn0, zn0);
            glBegin(GL_TRIANGLES);                         //draw triangles
              glNormal3f(xn0, yn0, zn0);                   //set normal of point
              glVertex3f(x0/SCALE, y0/SCALE, z0/SCALE);    //set point of triangle
              glNormal3f(xn1, yn1, zn1);                   //repeat for each point
              glVertex3f(x1/SCALE, y1/SCALE, z1/SCALE);
              glNormal3f(xn2, yn2, zn2);
              glVertex3f(x2/SCALE, y2/SCALE, z2/SCALE);    //SCALE resizes
            glEnd();
            /*glColor3f(0.0, 0.0, 0.0);                    //commented code for outlining shapes
            glBegin(GL_LINE_LOOP);
              glVertex3f(x0, y0, z0);
              glVertex3f(x1, y1, z1);
              glVertex3f(x2, y2, z2);
            glEnd();*/
          }

      }

  void Mesh::addVertex(Vertex *v) {
    // check if *v is in the mesh.
    if (find(vertices->begin(), vertices->end(), v) != vertices->end()) {
      return;
    }

    // add v if necessary.
    vertices->push_back(v);

  }

  void Mesh::addVertexNormal(Vertex *vn) {
    // check if *vn is in the mesh.
    if (find(vertexNormals->begin(), vertexNormals->end(), vn) != vertexNormals->end()) {
      return;
    }

    // add vn if necessary.
    vertexNormals->push_back(vn);
  }

  void Mesh::addFace(polygonFace f, bool checkOrientation) {
    // check if the face is in the mesh.
    //if (find(faces->begin(), faces->end(), f) != faces->end()) {
    //return;
    //}

    // if needed, check if the face has the proper orientation. Otherwise return.
    if (checkOrientation) {
      /*
        @leezl Let's talk about this. Here's the thing. We can use a
        determinant calculation to check the orientation in 3-space,
        but to do so we need four points - the three points of the
        triangle and a point with respect to which we are calculating
        the orientation. Obviously, we would want this fourth point
        to be the camera, but that would mean that we would have to
        keep track of camera information, which at this early point
        in the loading process could be problematic.

        For the time being I'm just assuming that everything works
        out.
       */
    }

    //std::cout << "Face to add: " << f[0] << ", " << f[1] << ", " << f[2] << std::endl;

    // if needed, add the face to the mesh.
    faces->push_back(f);

  }

  void Mesh::setSmooth(bool u) {
    this->useSmooth = u;
  }

  std::string Mesh::toString() {
    std::stringstream ss;

    ss << "Vertex list:\n";
    std::vector<Vertex*>::iterator iter;
    std::vector<polygonFace>::iterator iter2;
    for (iter = vertices->begin(); iter != vertices->end(); ++iter) {
      ss << "(" << (*iter)->x << ", " << (*iter)->y << ", " << (*iter)->z << ", " << (*iter)->w << ")\n";
    }
    ss << "\nFace list:\n";
    for (iter2 = faces->begin(); iter2 != faces->end(); ++iter2) {
      ss << "(" << (*iter2)[0] << ", " << (*iter2)[1] << ", " << (*iter2)[2] << ")\n";
    }

    return ss.str();
  }

}
